package com.atguigu.tingshu.common.handler;

import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 全局异常处理类
 *
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局异常处理方法。
     * 该方法通过@ExceptionHandler注解指定处理所有继承自Exception的异常。当控制器或其他层抛出异常时，
     * 该方法将捕获这些异常并返回一个表示错误结果的对象。
     * 使用此方法可以保持代码的整洁和可维护性，因为它将异常处理逻辑集中在一个地方，而不是分散在应用程序的各个部分。
     *
     * @param e 抛出的异常对象，包含错误的详细信息。
     * @return 返回一个表示操作失败的结果对象。这个结果对象可以包含关于错误的更多信息，比如错误代码和错误消息。
     */
    @ExceptionHandler(Exception.class)
    public Result<Void> error(Exception e){
        // 记录异常信息到日志系统，以便于问题追踪和诊断。
        log.error("Exception occurred: {}", e.getMessage(), e);
        // 返回一个失败的结果对象，表示操作由于异常而未能成功完成。
        return Result.fail();
    }

    /**
     * 自定义异常处理方法
     * @param e 错误信息
     * @return 返回值
     */
    @ExceptionHandler(GuiguException.class)
    public Result<Void> error(GuiguException e){
        return Result.build(null,e.getCode(), e.getMessage());
    }

    /**
     * 处理IllegalArgumentException的异常处理器。
     * 该方法专门用于处理程序运行过程中抛出的IllegalArgumentException异常。
     * 当遇到此类异常时，系统将返回一个特定的错误结果，而不是直接将异常信息暴露给前端。
     * 这样做可以增强系统的健壮性和安全性。
     *
     * @param e 抛出的IllegalArgumentException异常对象，包含具体的错误信息。
     * @return 返回一个封装了错误代码和消息的结果对象，其中不包含具体的业务数据。
     */
    @ExceptionHandler({IllegalArgumentException.class})
    public Result<Void> customIllegalArgumentException(Exception e) {
        // 记录异常信息到日志系统，以便后续的问题排查和分析。
        log.error("Exception occurred: {}", e.getMessage(), e);

        // 构建并返回一个表示参数验证错误的结果对象，不包含具体业务数据。
        return Result.build(null, ResultCodeEnum.ARGUMENT_VALID_ERROR);
    }

    /**
     * spring security异常
     * @param accessDeniedException 异常信息
     * @return 返回对象
     */
    @ExceptionHandler(AccessDeniedException.class)
    public Result<Void> error(AccessDeniedException e) throws AccessDeniedException {
        log.error("Exception occurred: {}", e.getMessage(), e);
        return Result.build(null, ResultCodeEnum.PERMISSION);
    }

    @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})
    public Result<Map<String, Object>> handleValidationExceptions(BindException exception) {
        return encapsulationException(exception);
    }

    /**
     * 封装验证异常信息为Result对象。
     * 当表单验证失败时，会抛出BindException异常。此方法旨在捕获此类异常，
     * 并将验证错误信息转换为一个映射，以便前端可以更方便地接收和展示错误信息。
     *
     * @param exception BindException实例，包含验证失败的详细信息。
     * @return Result对象，其中包含验证错误的映射和一个特定于验证错误的代码。
     */
    private Result<Map<String,Object>> encapsulationException(BindException exception){
        // 从BindException中获取验证结果对象
        BindingResult result = exception.getBindingResult();
        // 初始化一个映射，用于存储字段名和对应的错误信息
        Map<String, Object> errorMap = new HashMap<>();
        // 获取所有字段验证错误
        List<FieldError> fieldErrors = result.getFieldErrors();
        // 遍历所有字段错误，记录日志并添加到错误映射中
        fieldErrors.forEach(error -> {
            // 记录字段验证错误的日志
            log.error("field: {}, msg:{}", error.getField(), error.getDefaultMessage());
            // 将字段名和错误信息添加到错误映射中
            errorMap.put(error.getField(), error.getDefaultMessage());
        });
        // 使用错误映射和预定义的错误代码构建Result对象返回
        return Result.build(errorMap, ResultCodeEnum.ARGUMENT_VALID_ERROR);
    }


}
